package org.testfire.realm.util;

import com.jtheory.jdring.AlarmEntry;
import com.jtheory.jdring.AlarmListener;
import com.jtheory.jdring.AlarmManager;
import com.jtheory.jdring.PastDateException;
import org.testfire.realm.control.testfire.TestFireDBAO;
import org.testfire.realm.model.entities.testfire.AgentEntity;
import org.testfire.realm.model.entities.testfire.CrontaskEntity;
import org.testfire.realm.model.entities.testfire.TestRunnerQueueEntity;
import org.testfire.realm.model.entities.testfire.TestsetEntity;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import java.io.IOException;
import java.util.List;
import java.util.Vector;

/**
 * Created by IntelliJ IDEA.
 * User: granum
 * Date: Sep 10, 2007
 * Time: 4:46:03 AM
 */
public class CronTaskManager implements javax.servlet.Servlet {
    final AlarmManager mgr = new AlarmManager();
    final CronDispatcher dispatcher = new CronDispatcher();

    public CronTaskManager() {

    }

    public void init(ServletConfig servletConfig) throws ServletException {
        org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(CronTaskManager.class);
        logger.info("Loading available CrontaskEntity objects from database.");
        try {
            List<CrontaskEntity> tasks = TestFireDBAO.lookup().getCronTasks();
            for (CrontaskEntity task : tasks) {
                addCronTask(task);
            }
            logger.info(String.format("Found %1$d. tasks", tasks.size()));
        } catch (Exception e) {
            logger.error("Could not load tasks:", e);
        }
    }

    public void destroy() {
        org.apache.log4j.Logger logger = org.apache.log4j.Logger.getLogger(CronTaskManager.class);
        logger.info("\n\n\n\n\n\n\n\n\n\n\n");
    }

    /**
     * @param task The task to add.
     * @return True if the task was added, false if the task was already enqueued.
     */
    public boolean addCronTask(CrontaskEntity task) {
        if (task.isManual()) {
            // means run it NOW
            dispatcher.dispatch(task);
            return false;
        }
        String taskName = String.valueOf(task.getId());

        int[] min = toCronIntArray(task.getMin());
        int[] hour = toCronIntArray(task.getHour());
        int[] DOM = toCronIntArray(task.getDom());
        int[] month = toCronIntArray(task.getMonth());
        int[] DOW = toCronIntArray(task.getDow());


        AlarmEntry alarm;
        try {
            alarm = new AlarmEntry(taskName, min, hour, DOM, month, DOW, -1, dispatcher);


            if (mgr.containsAlarm(alarm)) {
                return false;
            }
            mgr.addAlarm(alarm);
        }
        catch (PastDateException e) {
            // Can't happen: year is repeated. So, it MIGHT be a while before the event happens, but it will!
            throw new RuntimeException(e);
        }
        return true;
    }

    private int[] toCronIntArray(String csv) {
        int[] aryI;
        if (!S.empty(csv)) {
            if (csv.trim().equals("*")) {
                aryI = new int[1];
                aryI[0] = -1;
            } else {
                String[] aryS = csv.split(",");
                aryI = new int[aryS.length];
                for (int i = 0; i < aryS.length; i++) {
                    // No trim; fields are space seperated, therefor a space in a trim is a user error.
                    aryI[i] = Integer.parseInt(aryS[i]);
                }
            }
        } else {
            aryI = new int[0];
        }

        return aryI;

    }


    private class CronDispatcher implements AlarmListener {

        private CronDispatcher() {
        }

        public void dispatch(CrontaskEntity task) {
            new CronTaskThread(task).start();
        }

        public void handleAlarm(AlarmEntry entry) {
            Integer id = Integer.valueOf(entry.getName());
            try {
                CrontaskEntity task = TestFireDBAO.getInstance().getCronTask(id);
                dispatch(task);
            }
            catch (Exception e) {
                // removed
                mgr.removeAlarm(entry);
            }
        }
    }


    private static class CronTaskThread implements Runnable {

        private final CrontaskEntity task;


        public CronTaskThread(CrontaskEntity task) {
            this.task = task;
        }




        public synchronized void start() {
            Thread t = new Thread(this);
            t.setPriority((Thread.MAX_PRIORITY + Thread.MIN_PRIORITY) / 2);
            t.setDaemon(true);
            t.start();
        }

        public void run() {
            /* @TODO implement a thread manager here. */
            List<TestRunnerQueueEntity> queues = new Vector<TestRunnerQueueEntity>(TestFireDBAO.getInstance().getQueues(task));
            for (TestRunnerQueueEntity queue : queues) {
                List<AgentEntity> agents = new Vector<AgentEntity>(queue.getAgents());
                List<TestsetEntity> testsets = new Vector<TestsetEntity>(queue.getTestsets());
                /* We have agents.size() agents to run queue.getTestsets().size() testsets */
                int agentIdx = 0;
                for (TestsetEntity testset : testsets) {
                    AgentEntity agent = agents.get(agentIdx);
                    


                    ++agentIdx;
                    if (agentIdx == agents.size()) {
                        agentIdx = 0;
                    }
                }


            }


        }
    }

    public ServletConfig getServletConfig() {
        return null;
    }

    public void service(ServletRequest servletRequest, ServletResponse servletResponse) throws ServletException, IOException {
    }

    public String getServletInfo() {
        return null;
    }

}
